package scalalabEdit;

import scalaExec.Interpreter.GlobalValues;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.TreeSet;
import java.util.Vector;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.Caret;
import javax.swing.text.Element;
import javax.swing.text.JTextComponent;
import scala.tools.nsc.interpreter.Parsed;
import scalaExec.gui.AutoCompletionFrame;
import scalaExec.gui.autoCompleteListHandler;
import scala.collection.Iterator;


public class EditorKeyMouseHandler extends MouseAdapter implements KeyListener
{
          String text;
	  int caretPos = 1;      // track the cursor position 
          int prevCaretPos = 1;
          JTextPane inputTextComponent;    // the component that keeps and handles the editing text
          static private String frozenInputConsoleContents;
          static private int posAutoCompletion;
          static private  char endCommandChar = ';';   // character denoting the end of the previous command
          private JList  topLevelResultsList;
          private JList  membersResultList;
         
        public EditorKeyMouseHandler()
	{
	}

     
        
         
    public void keyTyped(KeyEvent e){
       /* int  keyValue = e.getKeyChar();
       
        if (keyValue == KeyEvent.VK_F10);
                 display_help();      */
   }
    
   public static boolean isAlpha(char ch) {
     if (ch >= 'a' && ch <= 'z')
	   return true;
    if (ch >= 'A' && ch <= 'Z')
	   return true;
	return false;
   }
   
	/**Interpret key presses*/
    public void keyPressed(KeyEvent e)
    {
        int keyValue = e.getKeyCode();
        inputTextComponent = (JTextPane)e.getSource();  // editor's keystrokes have as source the inputTextComponent JTextComponent
        caretPos  = inputTextComponent.getCaretPosition();  // get the input cursor postion
        GlobalValues.myEdit.currentLineNumberLabel.setText("Line: "+GlobalValues.myEdit.linecnt);
        GlobalValues.myEdit.currentColumnNumberLabel.setText("Column: "+GlobalValues.myEdit.columnCnt);
         
        switch (keyValue) {
                
            case KeyEvent.VK_SPACE:   // check for possible coloring of text
            case KeyEvent.VK_ENTER:    
             GlobalValues.myEdit.styleLastIdText(GlobalValues.myEdit.textArea);
             scalalabEditor.documentEditsPendable = true;   // avoid exiting without asking for saving text
             break;
            
             /*    String currentText = inputTextComponent.getText();
                int lastLoc = currentText.length();
                int prevLoc = lastLoc-1;
                while (isAlpha(currentText.charAt(prevLoc)) && prevLoc>=0)
                        prevLoc--;
                scalalabEditor.styleText(inputTextComponent, prevLoc+1, lastLoc); 
                break;*/
            
           case KeyEvent.VK_F6:   // Execute Scala
                 GlobalValues.myEdit.executeScalaCommand();
                 
                 break;

          case KeyEvent.VK_F5:  // Execute Java with the internal compiler
                GlobalValues.myEdit.executeJavaCommand();
                 break;
                 
            case KeyEvent.VK_F4:  // Execute Java with the javac compiler
                GlobalValues.myEdit.executeExternalJavaCommand();
                 break;
                 
            case KeyEvent.VK_F8:   // Execute the whole text
                executeWholeText();
                break;
                    
            case KeyEvent.VK_F7:   // Execute the text following the previously executed
               executeTextFromPrevToCurrentLoc();
               break;
                       
                
               case KeyEvent.VK_F9:   // Execute the text from beginning to the cursor location
                 executeTextUpToCursor();
                 break;
                   
            case KeyEvent.VK_F2:   // Execute the selected text
                executeSelectionText();
                 break;

                    /* SOS here
            case  KeyEvent.VK_CONTROL & KeyEvent.VK_HOME:
                caretPos = inputTextComponent.getCaretPosition();
                text = inputTextComponent.getText();
                newLineCnt = 0;
                idx = 0;
                while (idx<caretPos)   {
                    if (text.charAt(idx) == '\n') 
                       newLineCnt++;
                    idx++;
                }
                scalalabEditor.linecnt = newLineCnt;
                scalalabEditor.currentCharacterNoLabel.setText("Char: "+caretPos+" ");
                scalalabEditor.currentLineNumberLabel.setText("Line: "+scalalabEditor.linecnt);
                break;
                */

            case KeyEvent.VK_TAB:
                
                
                    e.consume();
                        //get the text on the current line
                    text = inputTextComponent.getText();
          
                    // get the starting point of the current command start                    
                    int lastNewLineCommandStartIdx = text.lastIndexOf('\n')+1;
                
             // check for multiple commands per line       
                    int previousSemicolonCommandStartIdx = text.lastIndexOf(endCommandChar)+1;
                    if (previousSemicolonCommandStartIdx > lastNewLineCommandStartIdx)  {  // multiple commands per line separated by ';''
                        lastNewLineCommandStartIdx = previousSemicolonCommandStartIdx;
                     }
                    posAutoCompletion = lastNewLineCommandStartIdx;
                    String toksToCheckForLastIndex = "= +*-";
                    int toksCnt = toksToCheckForLastIndex.length();
                    for (int k=0; k<toksCnt; k++)  {
                    int tokIndex = text.lastIndexOf(toksToCheckForLastIndex.charAt(k))+1;  // index of token  (in order to match the text after it)
                    if (tokIndex > lastNewLineCommandStartIdx)  {
                        posAutoCompletion = tokIndex;
                        lastNewLineCommandStartIdx = tokIndex;
                      }
                    }
                   
                     text = text.substring(posAutoCompletion, text.length()).trim();
                     
                     // check for '.' that marks that the user requests to know the members of a structure
                     String iString = text; //= text.substring(startPos, text.length());
                     scala.Option<Class<?>> classOfItem;
                     String  afterDot="";
                     boolean membersRequested = false;  // when the user presses dot, we imply that the completion should provide the members of a structure
       
                     int dotIndex = text.indexOf('.');
                     if (dotIndex != -1)    {    // members of a structure should be provided
                         membersRequested = true;
                         frozenInputConsoleContents = inputTextComponent.getText();
                         int tabDotIndex = frozenInputConsoleContents.lastIndexOf('.');
                         frozenInputConsoleContents = frozenInputConsoleContents.substring(0, tabDotIndex+1);
                         afterDot = iString.substring(dotIndex+1, iString.length());    // get member typed after dot
                         afterDot = afterDot.trim();   // strip any blanks
                         iString = iString.substring(0, dotIndex);  // remove the dot
                     }
                     boolean afterDotCharsExist = false;
                     if (afterDot.length()>0)
                         afterDotCharsExist = true;

// create a completion object                    
                    scala.tools.nsc.interpreter.Completion  completion = new  scala.tools.nsc.interpreter.Completion(GlobalValues.globalInterpreter);
                    
                    if (membersRequested == false)   {   // provide the top level elements
               Parsed ps = Parsed.apply(iString);
               scala.collection.immutable.List<String> scalaResultsList = completion.topLevelFor(ps);
               int matchedSize = scalaResultsList.size();
               if (matchedSize > 0) {  // top level elements exist, edit them
                     String []    dmatches =  new String[matchedSize];
                     Iterator  complResults = (Iterator) scalaResultsList.elements();
                     int k=0;
                     while (complResults.hasNext())  {   // collect the matched results
                         String currentCompletion = (String)complResults.next();
                         dmatches[k++] = currentCompletion;
                  }
                    
                topLevelResultsList = new JList(dmatches);
                topLevelResultsList.addListSelectionListener(new ListSelectionListener() {
                     public void valueChanged(ListSelectionEvent lse) {
                             
                             String  selValue = topLevelResultsList.getSelectedValue().toString();
                             
                             String currentText = inputTextComponent.getText();
                             int    posPreviousText  =  currentText.lastIndexOf('\n') + 2;
                             int    posEndPreviousCommand = currentText.lastIndexOf(endCommandChar)+1;
                             if  (posEndPreviousCommand > posPreviousText)
                                 posPreviousText = posEndPreviousCommand;
                             //int lastEqualsIdx = currentText.lastIndexOf('=')+1;
                             if (posAutoCompletion > posPreviousText)
                                 posPreviousText = posAutoCompletion;
                             inputTextComponent.setText(currentText.substring(0,posPreviousText)+selValue);
                             GlobalValues.selectedStringForAutoCompletion = selValue;
                             inputTextComponent.setCaretPosition(inputTextComponent.getText().length()-1);   	// set cursor at the end of the text area
                             
	                 }
                     }
         );
        
         GlobalValues.autoCompletionFrame = new AutoCompletionFrame("scalalabConsole AutoCompletion, Workspace variables");
         GlobalValues.autoCompletionFrame.displayMatches(topLevelResultsList);
                     }     //  top level elements exist, edit them
                    }   //  // provide the top level elements
                   
                    else   {   // a dot pressed which implies that the completion should provide members of a structure
                        
                    //  see if we have methods available
                    scala.collection.immutable.List<String> scalaComplResultsList = completion.completions(iString);

                     classOfItem = scalaExec.Interpreter.GlobalValues.globalInterpreter.clazzForIdent(iString);
                     if (classOfItem.toString().equalsIgnoreCase("None") == false )  {
                    // completion.
                    Iterator  ccomplResults = scalaComplResultsList.elements();
                    int membersSize = ccomplResults.size();
                    if (membersSize > 0)  {   // member elements exist, edit them 
                        Vector  memberMatches = new Vector();
                        Iterator  complResults = scalaComplResultsList.elements();
                        while (complResults.hasNext())  {   // collect the matched results
                            String currentCompletion = (String) complResults.next();
                            currentCompletion = currentCompletion.trim();
                            if (afterDotCharsExist)  {   // user typed after "."
                             if (currentCompletion.startsWith(afterDot))  {
                                String [] classMatchedMethods =  scalaExec.gui.ConsoleKeyHandler.getMethodsOfClass(classOfItem, currentCompletion);
                                int classMatchedMethodsLen = classMatchedMethods.length;
                                
                                for (int matchedCnt=0; matchedCnt<classMatchedMethodsLen; matchedCnt++)
                                  memberMatches.add( (String)classMatchedMethods[matchedCnt]);
                                }
                            }
                            else  {
                                String [] classMatchedMethods =  scalaExec.gui.ConsoleKeyHandler.getMethodsOfClass(classOfItem, currentCompletion);
                                int classMatchedMethodsLen = classMatchedMethods.length;
                                
                                for (int matchedCnt=0; matchedCnt<classMatchedMethodsLen; matchedCnt++)
                                  memberMatches.add((String) classMatchedMethods[matchedCnt]);
                                
          //                      if (classMatchedMethodsLen==0)   // a special Scala method, e.g. "+"
            //                        memberMatches.add(currentCompletion);
                            }
                          }   // collect the matched results
                        
                    membersResultList = new JList(memberMatches);
                    membersResultList.addListSelectionListener(new ListSelectionListener() {
                    public void valueChanged(ListSelectionEvent lse) {
                          Object selValueObj = membersResultList.getSelectedValue();
                          if (selValueObj != null)  {
                          String  selValue = (String)selValueObj;
                          inputTextComponent.setText(frozenInputConsoleContents+selValue);
                          GlobalValues.selectedStringForAutoCompletion = selValue;
                          inputTextComponent.setCaretPosition(inputTextComponent.getText().length());   	// set cursor at the end of the text area  
                          }
                          /*String currentText = inputConsole.getText();
                          int    posPreviousText  =  currentText.lastIndexOf(GlobalValues.scalalabPromptChar) + 2;
                          int    posEndPreviousCommand = currentText.lastIndexOf(endCommandChar)+1;
                          if  (posEndPreviousCommand > posPreviousText)
                                 posPreviousText = posEndPreviousCommand;
                          int lastEqualsIdx = currentText.lastIndexOf('.')+1;
                          if (lastEqualsIdx > posPreviousText)
                                 posPreviousText = lastEqualsIdx;
                          inputConsole.setText(currentText.substring(0,posPreviousText)+" "+selValue);
                          GlobalValues.selectedStringForAutoCompletion = selValue;
                          inputConsole.setCaretPosition(inputConsole.getText().length());   	// set cursor at the end of the text area  
         
	                 }  */
                    }
                 }
         );             
             GlobalValues.autoCompletionFrame = new AutoCompletionFrame(" Members of "+iString);
             GlobalValues.autoCompletionFrame.displayMatches(membersResultList);
                     };   // a dot pressed which implies that the completion should provide members of a structure
             
                   }   // member elements exist, edit them 
                    }
                    break;
                    
            case KeyEvent.VK_F3:
                GlobalValues.myEdit.executedTextStyledExist  = false;   // not styled executed text exist, thus allow formating
                if (GlobalValues.myEdit.formattedText)   // styled text is avoided for large documents
                    GlobalValues.myEdit.styleAllText(GlobalValues.myEdit.textArea);
                break;

               
            case KeyEvent.VK_F1:
                    e.consume();  // consume this event so it will not be processed in the default manner by the source that originated it
                  		//get the text on the current line
                    text = inputTextComponent.getText();
                    final char newLine = '\n';
                    boolean  multipleCommands = false;   // multiple commands at the same line
                    int newLineIdx = text.lastIndexOf(newLine)+1;
                    previousSemicolonCommandStartIdx = text.lastIndexOf(endCommandChar)+1;
                    lastNewLineCommandStartIdx = newLineIdx;
                    if (previousSemicolonCommandStartIdx > newLineIdx)  {  // multiple commands per line separated by ';''
                        lastNewLineCommandStartIdx = previousSemicolonCommandStartIdx;
                        multipleCommands = true;
                     }
                    int  eqIndex = text.lastIndexOf('=')+1;  // index of equality  (in order to match the text after it
                    int  startPos = lastNewLineCommandStartIdx;
                    if (eqIndex > lastNewLineCommandStartIdx) 
                        lastNewLineCommandStartIdx = eqIndex;
                    
                    text = text.substring(lastNewLineCommandStartIdx, text.length());
                    String inputString = text; //= text.substring(startPos, text.length());
                    inputString = inputString.trim();
                    System.out.println("inputString ="+inputString);

                    String [] matches = null;
                    if (keyValue==KeyEvent.VK_F1) 
                       matches = scalaExec.Interpreter.GlobalValues.autoCompletionScalaSci.getMatched(inputString);
                    else 
                       matches = scalaExec.Interpreter.GlobalValues.autoCompletionScalaSci.getMatchedRegEx(inputString);
                     
                    
                     final JList  resultsList = new JList(matches);
                     autoCompleteListHandler  detailHelpAdapter = new autoCompleteListHandler();
                     resultsList.addKeyListener(detailHelpAdapter); 
                     
                     
                     resultsList.addListSelectionListener(new ListSelectionListener() {
                         public void valueChanged(ListSelectionEvent e) {
                             String  selValue = resultsList.getSelectedValue().toString();
                                 selValue = selValue.substring(0, selValue.indexOf("."));
                     
                             String currentText = inputTextComponent.getText();
                             int    posPreviousText  =  currentText.lastIndexOf(newLine) + 1;
                             int    posEndPreviousCommand = currentText.lastIndexOf(endCommandChar)+1;
                             if  (posEndPreviousCommand > posPreviousText)
                                 posPreviousText = posEndPreviousCommand;
                             int lastEqualsIdx = currentText.lastIndexOf('=')+1;
                             if (lastEqualsIdx > posPreviousText)
                                 posPreviousText = lastEqualsIdx;
                             
                             GlobalValues.selectedStringForAutoCompletion = selValue;
                     
                             inputTextComponent.setText(currentText.substring(0,posPreviousText)+" "+selValue);
                             inputTextComponent.setCaretPosition(inputTextComponent.getText().length());   	// set cursor at the end of the text area
                             
                         }
                     }
                            );
                            
                GlobalValues.autoCompletionFrame = new AutoCompletionFrame("scalalabConsole AutoCompletion,  Help with manual added entries (Press F1  for detailed help on the selected entry)");
            
                            
                    GlobalValues.autoCompletionFrame.displayMatches(resultsList);
                     
                     break;
                     
            case KeyEvent.VK_ESCAPE:
                int i,j, lc;
            // Erase the current editing line
                    e.consume();
                    /*lc = input.getLineCount() - 1;
                    try
                        {
                            i = input.getLineStartOffset(lc);
                            j = input.getLineEndOffset(lc);
                            // Skip the prompt (i+2) and the end (j-1)
                            input.replaceRange("", i+2, j);
                        }
                    catch (Exception ex)
                        {
                            ex.printStackTrace();
                        }
                    this.commandIndex = this.commandsVector.size();  */
                    break;
                
                    
                
            default:  scalalabEditor.documentEditsPendable = true;   // avoid exiting without asking for saving text
                         break;
                
          }
        prevCaretPos = caretPos;   
        
    }
   
    
    static void executeWholeText() {
                String expression =  GlobalValues.myEdit.textArea.getText();
                GlobalValues.scalalabMainFrame.scalalabConsole.setText(GlobalValues.scalalabMainFrame.scalalabConsole.getText()+expression+"\n");
                GlobalValues.scalalabMainFrame.scalalabConsole.displayPrompt();
                GlobalValues.scalalabMainFrame.scalalabConsole.interpretLine(expression);
                GlobalValues.myEdit.textArea.select(0, expression.length());
                GlobalValues.myEdit.textArea.setSelectedTextColor(Color.RED);
                scalaExec.gui.watchScalaWorkspace.displayScalaSciBinding(scalaExec.scalaLab.scalaLab.variablesWorkSpacePanel);
                GlobalValues.myEdit.executedTextStyledExist = true;
    }
    
   static void executeTextFromPrevToCurrentLoc() {
                String expression =  GlobalValues.myEdit.textArea.getText();
                int newLocation =  GlobalValues.myEdit.textArea.getCaretPosition();
                int prevLocation = GlobalValues.myEdit.locPrevExecutedText;
                System.out.println("new = "+newLocation+", prev = "+prevLocation);
                expression = expression.substring( prevLocation, newLocation);
                System.out.println("interpreting expression = "+expression);
                GlobalValues.myEdit.locPrevExecutedText = newLocation;    // update last location of text to exeute
                GlobalValues.scalalabMainFrame.scalalabConsole.setText(GlobalValues.scalalabMainFrame.scalalabConsole.getText()+expression+"\n");
                GlobalValues.scalalabMainFrame.scalalabConsole.displayPrompt();
                GlobalValues.scalalabMainFrame.scalalabConsole.interpretLine(expression);
                GlobalValues.myEdit.textArea.select(prevLocation, newLocation);
                GlobalValues.myEdit.styleExecutedText(GlobalValues.myEdit.textArea, prevLocation, newLocation);
                GlobalValues.myEdit.textArea.setSelectedTextColor(Color.RED);
                GlobalValues.myEdit.locPrevExecutedText = newLocation;   // update location of code executed
                scalaExec.gui.watchScalaWorkspace.displayScalaSciBinding(scalaExec.scalaLab.scalaLab.variablesWorkSpacePanel);
                GlobalValues.myEdit.executedTextStyledExist = true;
    }
                
   static void executeTextUpToCursor() {
                String expression =  GlobalValues.myEdit.textArea.getText();
                int pos = GlobalValues.myEdit.textArea.getCaretPosition();
                expression = expression.substring(0, pos);   // extract the text up to the current location
                GlobalValues.scalalabMainFrame.scalalabConsole.setText(GlobalValues.scalalabMainFrame.scalalabConsole.getText()+expression+"\n");
                GlobalValues.scalalabMainFrame.scalalabConsole.displayPrompt();
                GlobalValues.scalalabMainFrame.scalalabConsole.interpretLine(expression);
                GlobalValues.myEdit.locPrevExecutedText = pos;   // update location of code executed
                GlobalValues.myEdit.textArea.select(0, pos);
                GlobalValues.myEdit.styleExecutedText(GlobalValues.myEdit.textArea, 0, pos);    // style the text as executed
                GlobalValues.myEdit.textArea.setSelectedTextColor(Color.RED);
                scalaExec.gui.watchScalaWorkspace.displayScalaSciBinding(scalaExec.scalaLab.scalaLab.variablesWorkSpacePanel);
                GlobalValues.myEdit.executedTextStyledExist = true;
    }
                
   static void executeSelectionText() {
                    String expression =  GlobalValues.myEdit.textArea.getSelectedText();
                    String wholeText = GlobalValues.myEdit.textArea.getText();
                    int startSelectionIdx = wholeText.indexOf(expression);
                    int startSelectionEnd = startSelectionIdx + expression.length();
                    GlobalValues.myEdit.locPrevExecutedText  = startSelectionEnd;    // update last location of text to exeute
                    GlobalValues.scalalabMainFrame.scalalabConsole.setText(GlobalValues.scalalabMainFrame.scalalabConsole.getText()+expression+"\n");
                    GlobalValues.scalalabMainFrame.scalalabConsole.displayPrompt();
                    GlobalValues.scalalabMainFrame.scalalabConsole.interpretLine(expression);
                    scalaExec.gui.watchScalaWorkspace.displayScalaSciBinding(scalaExec.scalaLab.scalaLab.variablesWorkSpacePanel);
                    GlobalValues.myEdit.styleExecutedText(GlobalValues.myEdit.textArea, startSelectionIdx, startSelectionEnd);
                    GlobalValues.myEdit.textArea.setSelectedTextColor(Color.RED);
                    GlobalValues.myEdit.executedTextStyledExist = true;
                }
                
public static  int getColumnAtCaret(JTextComponent component)

{

int caretPosition = component.getCaretPosition();

Element root = component.getDocument().getDefaultRootElement();

int line = root.getElementIndex( caretPosition );

int lineStart = root.getElement( line ).getStartOffset();

return caretPosition - lineStart + 1;

}

    // mouse clicked and caret postion at  text changed: update line count for the new position
        public void mouseClicked(MouseEvent me)
        {
             JTextPane inputTextComponent = ((JTextPane)me.getSource());
     
             if (GlobalValues.myEdit.formattedText)   // styled text is avoided for large documents
                 if (GlobalValues.myEdit.executedTextStyledExist == false)  // avoid destroying the styled of execeuted text segments
                    GlobalValues.myEdit.styleAllText(inputTextComponent);
             
             caretPos = inputTextComponent.getCaretPosition();
             String text = inputTextComponent.getText();
         
        try {
            GlobalValues.myEdit.linecnt =  scalalabEditor.getLineAtCaret(inputTextComponent) ;
            GlobalValues.myEdit.columnCnt =  scalalabEditor.getColumnAtCaret(inputTextComponent);
            GlobalValues.myEdit.currentLineNumberLabel.setText("Line: "+GlobalValues.myEdit.linecnt);
            GlobalValues.myEdit.currentColumnNumberLabel.setText("Column: "+GlobalValues.myEdit.columnCnt);
        }
        catch(Exception ex) {
            ex.printStackTrace();
        }
                
          }
    
    
    void display_help() {
        JFrame helpFrame = new JFrame("ScalaLab help");
        helpFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        helpFrame.setSize(400, 400);
        Container container = helpFrame.getContentPane();
        JTextArea helpText = new JTextArea();

        int classCnt = 0;
        Hashtable  clTable= new Hashtable(); //// SOS-StergscalaExec.Interpreter.GlobalValues.functionManager.getClassLoader().loadedClasses;
        Enumeration enumer = clTable.elements();
        TreeSet  sortedClasses =  new TreeSet();
        while(enumer.hasMoreElements())
		{
		    Object next = (Object)enumer.nextElement();
		    Class currentClass = (Class)next;
                    String className = currentClass.getCanonicalName();
                    sortedClasses.add(className);
                    classCnt++;
        }

          java.util.Iterator iter = sortedClasses.iterator();
          while (iter.hasNext()) {
                    String className = (String)iter.next();
                    helpText.append(className+"\n");
            }
          JScrollPane  helpPane = new JScrollPane(helpText);
        
        container.add(helpPane);
        helpFrame.setVisible(true);  
                
      }
    
        
    
    public void keyReleased(KeyEvent e)
    {
    	        
    }	
    
}
